﻿/*
 * KLF Headless Browser
 * Written by Kris Oye <kristianoye@gmail.com>
 * Copyright (C) 2011-2012.  All rights reserved.
 */
namespace KLF.Lib.Browser.DOM.HTML
{
    #region Using Statements

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using KLF.Lib.Browser.DOM.Events;
    using System.Globalization;

    #endregion

    /// <summary>
    /// Common class for all HTML form controls
    /// (since they have so much in common).
    /// </summary>
    abstract public class HTMLControlBase : HTMLFormElementBase
    {
        #region Constructors

        internal HTMLControlBase(Document owner, string tagName)
            : base(owner, tagName)
        {
        }

        #endregion

        #region DOM Level 1 Compliance

        public override void setAttribute(string name, string value)
        {
            if (name.Equals(HTMLConstants.HTML_ATT_SELECTED) && this is HTMLOptionElement)
            {
                if ((int)Owner._state <= (int)DocumentState.Interactive)
                {
                    if (String.IsNullOrEmpty(getAttribute(HTMLConstants.HTML_ATT_DEFAULT_SELECTED)))
                    {
                        String vTest = value.ToLowerInvariant();

                        base.setAttribute(HTMLConstants.HTML_ATT_DEFAULT_SELECTED,
                            (vTest.Equals(HTMLConstants.HTML_ATT_SELECTED) ||
                            vTest.Equals("true") || vTest.Equals("1") ?
                                HTMLConstants.HTML_ATT_SELECTED : "not-selected"));
                    }
                }
            }
            else if (name.Equals(HTMLConstants.HTML_ATT_VALUE))
            {
                if ((int)Owner._state <= (int)DocumentState.Interactive)
                {
                    if (String.IsNullOrEmpty(getAttribute(HTMLConstants.HTML_ATT_DEFAULT_VALUE)))
                    {
                        base.setAttribute(HTMLConstants.HTML_ATT_DEFAULT_VALUE, value);
                    }
                }
            }
            else if (name.Equals(HTMLConstants.HTML_ATT_CHECKED))
            {
                if ((int)Owner._state < (int)DocumentState.Interactive)
                {
                    if (String.IsNullOrEmpty(getAttribute(HTMLConstants.HTML_ATT_DEFAULT_CHECKED)))
                    {
                        String vTest = value.ToLowerInvariant();

                        base.setAttribute(HTMLConstants.HTML_ATT_DEFAULT_CHECKED, (
                            vTest.Equals(HTMLConstants.HTML_ATT_CHECKED) || 
                            vTest.Equals("true") || 
                            vTest.Equals("1") ? HTMLConstants.HTML_ATT_CHECKED : ""));
                    }
                }
            }
            else if (name.Equals(HTMLConstants.HTML_ATT_DISABLED))
            {
                String vTest = value.ToLowerInvariant();

                base.setAttribute(HTMLConstants.HTML_ATT_DISABLED, (
                    vTest.Equals(HTMLConstants.HTML_ATT_DISABLED) || 
                    vTest.Equals("true") || 
                    vTest.Equals("1") || 
                    vTest.Length == 0 ? HTMLConstants.HTML_ATT_DISABLED : ""));
            }
            base.setAttribute(name, value);
        }

        #endregion

        #region DOM Level 2 HTML Compliance

        /// <summary>
        /// A single character access key to give access to the form control.
        /// See the accesskey attribute definition in HTML 4.01.
        /// </summary>
        public string accessKey
        {
            get
            {
                return getAttribute(HTMLConstants.HTML_ATT_ACCESSKEY); 
            }
            set
            {
                if (value.Length == 0)
                    removeAttribute(HTMLConstants.HTML_ATT_ACCESSKEY);
                else
                    setAttribute(HTMLConstants.HTML_ATT_ACCESSKEY, value);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public bool defaultChecked
        {
            get
            {
                return getAttribute(HTMLConstants.HTML_ATT_DEFAULT_CHECKED)
                    .Equals(HTMLConstants.HTML_ATT_CHECKED);
            }
            set
            {
                setAttribute(HTMLConstants.HTML_ATT_DEFAULT_CHECKED,
                    value ? HTMLConstants.HTML_ATT_CHECKED : "");
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public string defaultValue
        {
            get
            {
                return getAttribute(HTMLConstants.HTML_ATT_DEFAULT_VALUE);
            }
            set
            {
                setAttribute(HTMLConstants.HTML_ATT_DEFAULT_VALUE, value);
            }
        }

        /// <summary>
        /// The control is unavailable in this context. See the disabled 
        /// attribute definition in HTML 4.01.
        /// </summary>
        public bool disabled
        {
            get
            {
                return getAttribute(HTMLConstants.HTML_ATT_DISABLED)
                    .ToLowerInvariant()
                    .Equals(HTMLConstants.HTML_ATT_DISABLED);
            }
            set
            {
                if (!value)
                    removeAttribute(HTMLConstants.HTML_ATT_DISABLED);
                else
                    setAttribute(HTMLConstants.HTML_ATT_DISABLED, HTMLConstants.HTML_ATT_DISABLED);
            }
        }

        /// <summary>
        /// Form control or object name when submitted with a form. See the 
        /// name attribute definition in HTML 4.01.
        /// </summary>
        public string name
        {
            get
            {
                return getAttribute(HTMLConstants.HTML_ATT_NAME);
            }
            set
            {
                setAttribute(HTMLConstants.HTML_ATT_NAME, value);
            }
        }

        /// <summary>
        /// Index that represents the element's position in the tabbing order.
        /// See the tabindex attribute definition in HTML 4.01.
        /// </summary>
        public int tabIndex
        {
            get
            {
                int _tabIndex;

                if (!Int32.TryParse(getAttribute(HTMLConstants.HTML_ATT_TABINDEX), out _tabIndex))
                    return 1;

                return _tabIndex > 0 ? _tabIndex : 1;
            }
            set
            {
                int _tabIndex;

                if (Int32.TryParse(getAttribute(HTMLConstants.HTML_ATT_TABINDEX), out _tabIndex))
                {
                    base.setAttribute(HTMLConstants.HTML_ATT_TABINDEX, _tabIndex.ToString());
                }
            }
        }

        /// <summary>
        /// The current form control value. See the value attribute definition
        /// in HTML 4.01.
        /// </summary>
        public virtual string value
        {
            get
            {
                return getAttribute(HTMLConstants.HTML_ATT_VALUE);
            }
            set
            {
                setAttribute(HTMLConstants.HTML_ATT_VALUE, value);
            }
        }

        #endregion

        #region Custom DOM

        /// <summary>
        /// Simulates user input by generating appropriate events, etc.
        /// </summary>
        /// <param name="val"></param>
        internal virtual void SimulateInput(string val)
        {
            if (Owner.Context != null)
            {
                StringBuilder actualInput = new StringBuilder();

                //  Set focus before typing.
                focus();

                for (int i = 0; i < val.Length; i++)
                {
                    char c = val[i];

                    if (disabled)
                        throw new ArgumentException("Cannot modify contents of disabled control.");

                    if (this is HTMLInputElement)
                    {
                        HTMLInputElement e = this as HTMLInputElement;
                        if (actualInput.Length >= e.maxLength)
                            continue;
                    }
                    var args = new
                    {
                        bubbles = true,
                        cancelable = true,
                        target = this as IEventTarget,
                        view = Owner.Context.Window,
                        charCode = c,
                        key = c,
                        keyCode = c,
                        locale = CultureInfo.CurrentCulture.TwoLetterISOLanguageName,
                    };

                    if (!dispatchEvent(new KeyboardEvent(
                        HTMLConstants.HTML_EVENT_KEYDOWN, args))) continue;

                    if (!dispatchEvent(new KeyboardEvent(
                        HTMLConstants.HTML_EVENT_KEYUP, args))) continue;

                    if (!dispatchEvent(new KeyboardEvent(
                        HTMLConstants.HTML_EVENT_KEYPRESS, args))) continue;

                    actualInput.Append(c);
                }

                //  Actually set the control value
                value = actualInput.ToString();

                dispatchEvent(new Event(HTMLConstants.HTML_EVENT_CHANGE,
                    new
                    {
                        bubbles = true,
                        cancelable = false,
                        target = this as IEventTarget
                    }));
            }
        }

        public override string ToString()
        {
            return String.Format("[object {0}; Name: {1}, ID: {2}, Value: {3}]",
                this.GetType().Name,
                getAttribute(HTMLConstants.HTML_ATT_NAME),
                getAttribute(HTMLConstants.HTML_ATT_ID),
                value);
        }

        #endregion
    }
}
